Skip to content

Add wasm32-unknown-emscripten target support#2805

Draft
guybedford wants to merge 2 commits into
nix-rust:masterfrom
guybedford:emscripten
Draft

Add wasm32-unknown-emscripten target support#2805
guybedford wants to merge 2 commits into
nix-rust:masterfrom
guybedford:emscripten

Conversation

@guybedford

@guybedford guybedford commented Jul 11, 2026

Copy link
Copy Markdown

Draft PR for #2804.

Adds wasm32-unknown-emscripten as a target for nix, with a CI job running the full test suite under Node (stock target, -Zbuild-std threaded, JSPI, NODERAWFS/NODERAWSOCKETS). Opened as a draft since it depends on unreleased patchsets for both Emscripten and Rust's libc, including:

Requires a recent Rust nightly to include rust-lang/rust#158937.

Test Status

148 passing, 38 ignored at runtime, 5 compiled out.

Ignored (38):

  • test_unistd (11): test_fork_and_waitpid, test_wait, test_mkfifo, test_mkfifoat, test_mkfifoat_none, test_setgroups, test_acct, test_pipe2, test_user_into_passwd, test_ttyname, test_linkat_no_follow_symlink
  • test_pty (6): test_open_ptty_pair, test_read_ptty_pair, test_write_ptty_pair, test_openpty, test_openpty_with_termios, test_forkpty
  • test_socket (7): recvfrom::stream, test_recvmsg_sockaddr_un, test_scm_rights, test_scm_rights_single_cmsg_multiple_fds, test_sendmsg_empty_cmsgs, test_getsockname, test_socketpair
  • test_termios (3): test_tcgetattr_pty, test_output_flags, test_local_flags
  • test_wait (3): test_wait_signal, test_wait_exit, test_waitstatus_pid
  • test_fcntl flock (3): lock_and_drop, downgrade, upgrade
  • test_net (2): test_if_nametoindex, test_if_indextoname
  • test_resource (1): test_resource_limits_nofile
  • test_signal (1): test_thread_signal_swap
  • test_sockopt (1): is_socket_type_unix

Compiled out (5):

  • test_socket (1): can_open_routing_socket
  • test_signal (4): test_killpg_none, test_signal, test_thread_signal_set_mask, test_thread_signal_block

Early feedback on the approach very much welcome.

Adds wasm32-unknown-emscripten as a target for nix, plus a CI job that runs the
full test suite under Node.

Source changes to compile on emscripten (which behaves like a Linux/musl libc):

* ioctl: use the Linux ioctl module and a c_int ioctl_num_type on emscripten
  (src/sys/ioctl, src/sys/mod.rs).
* resource: enable getrlimit/setrlimit and the Resource enum via libc::rlimit.
* socket: include emscripten in the SockaddrStorage AF_UNIX (pathname/unnamed)
  decode arms.
* unistd: sethostname is unavailable on emscripten, so gate it out.

The suite spawns OS threads (signal and socket-peer tests), so std is rebuilt
with atomics via -Zbuild-std and linked -pthread with -sPROXY_TO_PTHREAD so the
main thread can block; JSPI (-sJSPI) provides the return-to-host suspension for
blocking reads/writes, and NODERAWFS/NODERAWSOCKETS back the filesystem and
sockets with node's. This needs nightly + rust-src and a JSPI-capable Node
(26+, or 22 with --experimental-wasm-jspi). No custom target spec is required:
nightly now emits the __main_argc_argv entry point (rust-lang/rust#158937).

Genuinely-unsupported operations are ignored with
#[cfg_attr(target_os = "emscripten", ignore)] rather than cfg'd out, covering
fork/wait, pty/termios, mkfifo, getpwnam/setgroups/acct, flock, if_nametoindex,
pipe2 FD_CLOEXEC, AF_UNIX SCM_RIGHTS/datagram, thread signal masking, and
setrlimit; a few tests are cfg'd out where the feature is entirely absent
(raw signal actions, socketpair) or the host differs (linkat symlink follow).

Temporary, until the dependencies land upstream:

* Cargo.toml patches libc to guybedford/libc#libc-0.2-emscripten for the
  emscripten externs (rust-lang/libc#5270).
* the CI job builds against the guybedford/emscripten `cf` fork, which carries
  the NODEFS/socket/blocking patches this target depends on
  (emscripten-core/emscripten#27305, #27306).

Suite result: 148 passed, 0 failed, 35 ignored under Node - green on Linux CI.
@guybedford guybedford force-pushed the emscripten branch 3 times, most recently from 199bda8 to cb64cba Compare July 11, 2026 01:07
Adds wasm32-unknown-emscripten as a target for nix, plus a CI job that runs the
full test suite under Node.

Source changes to compile on emscripten (which behaves like a Linux/musl libc):

* ioctl: use the Linux ioctl module and a c_int ioctl_num_type on emscripten
  (src/sys/ioctl, src/sys/mod.rs).
* resource: enable getrlimit/setrlimit and the Resource enum via libc::rlimit.
* socket: include emscripten in the SockaddrStorage AF_UNIX (pathname/unnamed)
  decode arms.
* unistd: sethostname is unavailable on emscripten, so gate it out.

The suite spawns OS threads (signal and socket-peer tests), so std is rebuilt
with atomics via -Zbuild-std and linked -pthread with -sPROXY_TO_PTHREAD so the
main thread can block; JSPI (-sJSPI) provides the return-to-host suspension for
blocking reads/writes, and NODERAWFS/NODERAWSOCKETS back the filesystem and
sockets with node's. This needs nightly + rust-src and a JSPI-capable Node
(26+, or 22 with --experimental-wasm-jspi). No custom target spec is required:
nightly now emits the __main_argc_argv entry point (rust-lang/rust#158937).

Unsupported operations are ignored with #[cfg_attr(target_os = "emscripten",
ignore)] rather than cfg'd out wherever they still compile, covering fork/wait,
pty/termios, mkfifo, getpwnam/setgroups/acct, flock, if_nametoindex, pipe2
FD_CLOEXEC, AF_UNIX SCM_RIGHTS/datagram, getsockname, socketpair, setrlimit, and
the linkat symlink-follow host check. The signal-delivery tests (signal, killpg,
thread mask) and the AF_ROUTE test are cfg'd out instead: compiling their
extern "C" handlers in perturbs the wasm indirect function table and aborts the
runtime.

Temporary, until the dependencies land upstream:

* the emscripten CI job injects a libc patch (guybedford/libc#libc-0.2-emscripten,
  rust-lang/libc#5270) via `cargo --config`, so other targets and CI jobs are
  unaffected.
* it also builds against the guybedford/emscripten `cf` fork, which carries the
  NODEFS/socket/blocking patches this target depends on
  (emscripten-core/emscripten#27305, #27306).

Suite result: 148 passed, 0 failed, 38 ignored under Node - green on Linux CI.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant